<?php
/**
 *
 * User: JSparrow
 * DateTime: 2016/12/16 14:12
 * Created by PhpStorm.
 */

namespace app\modules\admin\components;


use app\modules\admin\models\Route;
use Yii;
use yii\base\ActionFilter;
use yii\base\Module;
use yii\di\Instance;
use yii\web\ForbiddenHttpException;
use yii\web\User;

class AccessControl extends ActionFilter {
	private $_user = 'user';

	/**
	 * @var array List of action that not need to check access.
	 */
	public $allowActions = [];

	/**
	 * 说明:
	 * @param \yii\base\Action $action
	 * @return bool
	 */
	public function beforeAction($action)
	{
		$actionId = $action->getUniqueId();
		$user = $this->getUser();
		if(Route::checkRoute('/' . $actionId, Yii::$app->request->get(), $user)) {
			return true;
		}
		$this->denyAccess($user); // 处理通过的权限
		return false;
	}

	/**
	 * @return User
	 */
	public function getUser()
	{
		if(!$this->_user instanceof User) {
			$this->_user = Instance::ensure($this->_user, User::className());
		}

		return $this->_user;
	}

	/**
	 * @param mixed $user
	 */
	public function setUser($user)
	{
		$this->_user = $user;
	}

	/**
	 * 说明: 对授权不通过的操作进行处理
	 * @param $user User
	 * @throws ForbiddenHttpException
	 */
	protected function denyAccess($user)
	{
		if($user->isGuest) {
			$user->loginRequired();
		} else {
			throw new ForbiddenHttpException('你没有访问该功能的权限');
		}
	}


	/**
	 * 说明: 判断是否要启用过滤器
	 * 		判断哪些权限是可以不用进行权限验证的.
	 * 		跳过allowActions中的路由
	 * 		跳过controller中allowAction方法中的路由
	 * @param \yii\base\Action $action
	 * @return bool
	 */
	protected function isActive($action)
	{
		$uniqueId = $action->getUniqueId();
		if($uniqueId === Yii::$app->errorHandler->errorAction) {
			return false;
		}

		$user = $this->getUser();
		if($user->isGuest
			&& is_array($user->loginUrl)
			&& isset($user->loginUrl[0])
			&& $uniqueId === trim($user->loginUrl[0], '/')) {
			return false;
		}

		if($this->owner instanceof Module) {
			$mid = $this->owner->getUniqueId();
			$id = $uniqueId;
			if ($mid !== '' && strpos($id, $mid) === 0) {
				$id = substr($id, strlen($mid) + 1);
			}
		} else {
			$id = $action->id;
		}
		foreach ($this->allowActions as $route) {
			if (substr($route, -1) === '*') {
				$route = rtrim($route, '*');
				if($route === '' || strpos($id, $route) === 0) {
					return false;
				}
			} else {
				if($id === $route) {
					return false;
				}
			}
		}
		if ($action->controller->hasMethod('allowAction') && in_array($action->id, $action->controller->allowAction())) {
			return false;
		}

		return true;
	}
}